package spring.cloud.tasks.client.item_task_processor.task_items_processor;

import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import spring.cloud.tasks.client.item_task_processor.Task;
import spring.cloud.tasks.client.item_task_processor.TaskItemAndParameter;
import spring.cloud.tasks.client.item_task_processor.TaskProcessor;
import spring.cloud.tasks.client.item_task_processor.task_items_processor.task_data_processor.SingleTaskDataProcessor;
import spring.cloud.tasks.client.item_task_processor.task_items_processor.task_data_processor.TaskDataProcessor;

import java.lang.reflect.Array;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

@Slf4j
public class WaitWhenNoDataModeTaskItemsProcessor<TaskDataId> implements TaskItemsProcessor {

    @Setter
    @Getter
    Task task;
    @Setter
    @Getter
    protected volatile TaskProcessor ruleTaskProcessor;
    @Setter
    @Getter
    protected volatile TaskDataProcessor<TaskDataId> taskDataProcessor;
    @Setter
    @Getter
    volatile boolean multipleTaskDataProcessor = false;

    @Setter
    @Getter
    volatile boolean isSleeping = false;
    @Setter
    @Getter
    volatile boolean destroy = false;
    @Setter
    @Getter
    volatile Statistics statistics;
    @Setter
    @Getter
    volatile List<Thread> threadList = Collections.synchronizedList(new ArrayList<Thread>());
    final Signal signal = new Signal();
    @Setter
    @Getter
    protected volatile List<TaskDataId> taskDataIdList = Collections.synchronizedList(new ArrayList<TaskDataId>());


    public WaitWhenNoDataModeTaskItemsProcessor(//
                                                TaskProcessor ruleTaskProcessor,//
                                                TaskDataProcessor<TaskDataId> taskDataProcessor,//
                                                Statistics statistics) throws Exception//
    {
        this.task = this.ruleTaskProcessor.getTask();
        this.ruleTaskProcessor = ruleTaskProcessor;
        this.taskDataProcessor = taskDataProcessor;
        if (this.taskDataProcessor instanceof SingleTaskDataProcessor<?>) {
            task.setMaxExecuteNum(1);//自动修正
            multipleTaskDataProcessor = false;
        } else {
            multipleTaskDataProcessor = true;
        }
        this.statistics = statistics;
        //
        updateTaskItemCurrentProcessorProgressInfoAtTaskProgressThread();
        //
        for (int i = 0; i < ruleTaskProcessor.getThreadCountPerTaskProcessor(); i++) {
            this.newThread(i);
        }
    }

    public void updateTaskItemCurrentProcessorProgressInfoAtTaskProgressThread() {
        ruleTaskProcessor.updateTaskItemCurrentProcessorProgressInfoAtTaskProgressThread();
    }


    private void newThread(int index) {
        String threadName = //
                this.ruleTaskProcessor.getTaskProcessorInfo().getTaskId() + "-"//
                        + this.ruleTaskProcessor.getTaskProcessorInfo().getProcessorId() + "-" + index;//
        WaitWhenNoDataModeTaskProcessingThread thread = new WaitWhenNoDataModeTaskProcessingThread(this);//共享信息
        thread.setName(threadName);
        thread.setDaemon(true);
        thread.start();
        threadList.add(thread);
    }


    public boolean isSleeping() {
        return this.isSleeping;
    }

    public boolean noTaskDatas() {
        return this.taskDataIdList.size() == 0;
    }

    public void destroy() throws Exception {
        this.destroy = true;
        this.taskDataIdList.clear();
    }

    public void clearTaskDataIdList() {
        this.taskDataIdList.clear();
    }

    //


    public synchronized Object getTaskDataId() {
        if (this.taskDataIdList.size() == 0) {
            return null;
        } else {
            return this.taskDataIdList.remove(0);//第一个
        }
    }

    public synchronized Object[] getTaskDataIds() {
        if (this.taskDataIdList.size() == 0) {
            return null;
        }
        int minSizeBetweenTaskListSizeAndMaxExecuteNumber = taskDataIdList.size() > task.getMaxExecuteNum() ? task.getMaxExecuteNum() : taskDataIdList.size();
        Object[] taskDataIdList = null;
        if (!(minSizeBetweenTaskListSizeAndMaxExecuteNumber > 0)) {
            return taskDataIdList;
        } else {
            taskDataIdList = (Object[]) Array.newInstance(this.taskDataIdList.get(0).getClass(), minSizeBetweenTaskListSizeAndMaxExecuteNumber);
            for (int i = 0; i < minSizeBetweenTaskListSizeAndMaxExecuteNumber; i++) {
                taskDataIdList[i] = this.taskDataIdList.remove(0);//第一个
            }
            return taskDataIdList;
        }

    }

    /**
     * 获取任务
     *
     * @return 任务数量
     */
    protected int getTaskDataIdsFromDb() {
        //
        updateTaskItemCurrentProcessorProgressInfoAtTaskProgressThread();
        //
        try {
            if (this.task.getSleepTimeWhenNoData() > 0) {
                this.isSleeping = true;
                Thread.sleep(task.getSleepTimeWhenNoData());
                this.isSleeping = false;
            }

            List<TaskItemAndParameter> taskItemAndParameterList = this.ruleTaskProcessor.getTaskItemAndParameterListUntilTaskDataListIsAllFinshed();
            if (taskItemAndParameterList.size() > 0) {
                List<TaskDataId> taskDataIdList = this.taskDataProcessor.getTaskDataList(//
                        task.getTaskParameter(),//
                        taskItemAndParameterList, //
                        this.ruleTaskProcessor.getTaskItemListSizeInZooKeeper(), //
                        task.getFetchNum()//
                        //
                );//
                ruleTaskProcessor.getTaskProcessorInfo().setLastFetchDataTime(new Timestamp(ruleTaskProcessor.zooKeeperDataManager.zooKeeperCurrentTimeMillis()));
                if (taskDataIdList != null) {
                    this.taskDataIdList.addAll(taskDataIdList);
                }
            } else {
            }
            addTaskFetchNum(taskDataIdList.size());
            return this.taskDataIdList.size();
        } catch (Throwable ex) {
            log.error("Get tasks error.", ex);
        }
        return 0;
    }

    //
    public void addTaskFetchNum(long num) {

        this.statistics.addFetchDataCount(1);
        this.statistics.addFetchTaskFetchNum(num);
    }

    public void addTaskSuccessNum(long num, long spendTime) {
        this.statistics.addTaskSuccessNum(num);
        this.statistics.addSpendTime(spendTime);
    }

    public void addTaskFailNum(long num, long spendTime) {
        this.statistics.addTaskFailNum(num);
        this.statistics.addSpendTime(spendTime);
    }
}
